﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using WcfServer.Classes.Client;


namespace Client.ViewModel
{
    class DirectConnectionViewModel : ViewModelBase
    {
        private string directIP;
        public string DirectIP
        {
            set
            {
                if (directIP != value)
                {
                    directIP = value;
                    base.OnPropertyChanged("DirectIP");
                }
            }
            get
            {
                return directIP;
            }
        }
        private string srcName;
        public string SrcName
        {
            set
            {
                if (srcName != value)
                {
                    srcName = value;
                    base.OnPropertyChanged("SrcName");
                }
            }
            get
            {
                return srcName;
            }
        }
        public ICommand connectCommand;
        public ICommand ConnectCommand
        {
            get
            {
                if (connectCommand == null)
                {
                    connectCommand = new RelayCommand(param => this.Connect(), param => this.CanConnect);
                }
                return connectCommand;
            }
        }
        public void Connect()
        {
            Task.Factory.StartNew(delegate
            {

                Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                soc.Connect(IPAddress.Parse(DirectIP), 24300);

                DirectInitiator di = new DirectInitiator(SrcName);

                FileStream fs = new FileStream(@"server_public.xml", FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs);
                String XML = sr.ReadToEnd();

                InitMsgWrapper msg = new InitMsgWrapper()
                {
                    IsDirect = true,
                    EncryptedMessageContent = di.Send(XML)
                };

                soc.Send(msg.Send());

                byte[] buffer = new byte[10000]; //TODO: is 10KB enough?
                int bytesReceived = soc.Receive(buffer);

                byte[] data = new byte[bytesReceived];
                Buffer.BlockCopy(buffer, 0, data, 0, bytesReceived);

                Ack ack = new Ack(data, di.SessionKey);
                //TODO: VALIDATION.

                var dialogue = new ChatWindow(soc, di.SessionKey);
                dialogue.Show();
            }, TaskScheduler.FromCurrentSynchronizationContext());


        }
        private bool CanConnect
        {
            get { return true; }
        }

        private ICommand listenCommand;
        public ICommand ListenCommand
        {
            get
            {
                if (listenCommand == null)
                {
                    listenCommand = new RelayCommand(param => this.Listen(), param => this.CanListen);
                }
                return listenCommand;
            }
        }
        public void Listen()
        {
            Task.Factory.StartNew(delegate
            {
                var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 24300);
                listener.Bind(ipe);
                listener.Listen(4);
                while (true)
                {
                    var soc = listener.Accept();
                    //String src_ip = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();

                    HandleAcceptedDirect(soc);

                }
            });
        }
        private bool CanListen
        {
            get { return true; }
        }
        private void HandleAcceptedDirect(Socket soc)
        {
            byte[] buffer = new byte[10000]; //TODO: is 10KB enough?
            int bytesReceived = soc.Receive(buffer);

            byte[] data = new byte[bytesReceived];
            Buffer.BlockCopy(buffer, 0, data, 0, bytesReceived);

            InitMsgWrapper msg = new InitMsgWrapper(data);
            if (msg.IsDirect)
            {
                DirectInitiator di = new DirectInitiator(msg.EncryptedMessageContent, SrcName);//TODO: what should be use for container name? 
                //TODO: Check Sign Validity. Check Every Validation! Do we need an error handling?
                Ack ack = new Ack()
                {
                    TimeStamp = di.TimeStamp.AddSeconds(1),//T+1
                };

                byte[] ackToSend = ack.Send(di.SessionKey);// Ks ( T )
                soc.Send(ackToSend);

                var dialogue = new ChatWindow(soc, di.SessionKey);
                dialogue.Show();
            }
        }
    }
}
